package org.example.job;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.controller.PingController;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


@Slf4j
@Component
public class PingScheduler {

    private static final String LOCK_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "shared-lock1";
    private final PingController pingController;

    public PingScheduler(PingController pingController) {
        this.pingController = pingController;
        scheduleResetCounter();
    }

    @Scheduled(fixedRate = 1000)
    public void run() {
        try {
            log.info("执行时间: {}", DateUtil.now());
            lockAndProcess();
        } catch (Exception e) {
            log.error("Error in run method: "+e.getMessage(), e);
        }
    }

    private void lockAndProcess() {
        try (RandomAccessFile lockFile = new RandomAccessFile(LOCK_FILE_PATH, "rw");
             FileChannel channel = lockFile.getChannel()) {
            FileLock fileLock = null;
            try {
                fileLock = channel.tryLock();
                if (fileLock != null && fileLock.isValid()) {
                    long currentSecond = DateUtil.current() / 1000;
                    String data = lockFile.length() > 0 ? lockFile.readLine() : currentSecond + "#" + 0;
                    String[] split = data.trim().split("#");
                    final long fileRecordSeconds = Long.valueOf(split[0]).longValue();
                    final Integer fileRecordCount = Integer.valueOf(split[1]);

                    log.info("文件的内容: {},{}, 当前时间current: {}, 当前时间比对：{}", split[0], split[1],currentSecond, fileRecordSeconds == currentSecond);
                    if (fileRecordSeconds <= currentSecond && fileRecordCount < 2) {
                        Mono<ResponseEntity<String>> responseEntityMono = pingController.sendRequestToPongService();
                        lockFile.seek(0);
                        long value = fileRecordCount + 1;
                        lockFile.write((currentSecond + "#" + value).getBytes(StandardCharsets.UTF_8));
                        lockFile.setLength(lockFile.getFilePointer());
//                        log.info("获取到了锁，发起ping请求,结果: {}", responseEntityMono.block());
                        // 打印日志
                        responseEntityMono.doOnNext(responseEntity -> {
                            log.info("Response Status: {}, Response Body: {}", responseEntity.getStatusCode(), responseEntity.getBody());
                        }).subscribe();
                    } else {
                        log.warn("Reuqest not send as being rate limited");
                    }
                }
            }catch (IOException e) {
                log.error("Failed to acquire lock or read/write to the file: {}", e.getMessage());
            } finally {
                if (fileLock != null) {
                    try {
                        fileLock.release();
                    } catch (IOException e) {
                        log.error(e.getMessage(),e);
                    }
                }
            }
        }catch (IOException e) {
            log.error("Error accessing the lock file during send request: {}", e.getMessage());
        }

    }

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private void scheduleResetCounter() {
        scheduler.scheduleAtFixedRate(() -> {
            try (RandomAccessFile file = new RandomAccessFile(LOCK_FILE_PATH, "rw");
                 FileChannel channel = file.getChannel()) {
                FileLock lock = null;
                try {
                    // Try to acquire an exclusive lock on the file
                    lock = channel.tryLock();
                    if (lock != null && lock.isValid()) {
                        // Reset the counter to 0 for the next second
                        long currentSecond = DateUtil.current() / 1000;
                        file.seek(0);
                        file.write((currentSecond + "#" + 0).getBytes(StandardCharsets.UTF_8));
                        file.setLength(file.getFilePointer()); // Truncate any remaining data
                    }
                } catch (IOException e) {
                    log.error("Failed to reset counter: {}", e.getMessage());
                } finally {
                    if (lock != null) {
                        try {
                            lock.release();
                        } catch (IOException e) {
                            log.error("Failed to release lock during reset: {}", e.getMessage());
                        }
                    }
                }
            } catch (IOException e) {
                log.error("Error accessing the lock file during reset: {}", e.getMessage());
            }
        }, 0, 1, TimeUnit.SECONDS);
    }

}
